// （异步日志器）任务处理器
// 设计思想为：双缓冲区阻塞数据池
// 消费者对应的缓冲区负责处理数据和交换缓冲区，最开始没有数据。等待生产者生产数据，然后交换处理，再等待，以此循环
// 优势为：避免空间的频繁申请和释放，尽可能减少生产者和消费者直接的锁冲突概率，提高任务处理效率

#ifndef _M_LOOPER_H__
#define _M_LOOPER_H__

#include "buffer.hpp"
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <string>
#include <iostream>
#include <atomic>

namespace snowdragon
{
    // 异步任务处理器的安全模式，不安全的是扩容的操作，安全的是限定大小，写入的消息超过生产者缓冲区可写大小且尝试等待失败时会直接报错
    // 但我觉得报错太粗暴了，使用的是生产者条件变量等待的方式。即尝试失败会阻塞住
    enum class AsyncLooperSafeType
    {
        SAFE_ASYNC_LOOPER,  // 安全
        UNSAFE_ASYNC_LOOPER // 不安全
    };

    class AsyncLooper       // 任务处理器类
    {
    public:
        using functor = std::function<void(Buffer &)>; // 回调函数的类型
        using ptr = std::shared_ptr<AsyncLooper>;
        //  callback参数需要加const修饰和用引用
        AsyncLooper(const functor &callback, AsyncLooperSafeType safe_type = AsyncLooperSafeType::SAFE_ASYNC_LOOPER)
            : _run(true), _callback(callback), _safe_type(safe_type), _thread(std::thread(&AsyncLooper::threadEntrance, this)) // 需要加上&AsyncLooper::，否则会报错
        {
            // std::cout << "AsyncLooper()" <<std::endl;                // 调式时所写
            // std::cout << "main thread _run: " << _run <<std::endl;
        }
        ~AsyncLooper()
        {
            // std::cout << "~AsyncLooper()" <<std::endl;
            // std::cout << "main thread _run: " << _run <<std::endl;

            stop();             // 要结束了，处理剩余的东西
            _thread.join();     // 等待异步线程，即消费行为的结束

            // 扩容后，_producer_buff和_consumer_buff的大小会不相等
            // 测试时，要将buffer.hpp中对应的Buffer类成员变量改为public，因为会用到_buffer成员变量

            // std::cout << "_producer_buff.readableSize(): " << _producer_buff.readableSize()
            // << " _producer_buff.writeableSize(): " << _producer_buff.writeableSize()
            // << " _producer_buff._buffer.size():" << _producer_buff._buffer.size() << std::endl;

            // std::cout << "_consumer_buff.readableSize(): " << _consumer_buff.readableSize()
            // << " _consumer_buff.writeableSize(): " << _consumer_buff.writeableSize()
            // << " _consumer_buff._buffer.size():" << _consumer_buff._buffer.size() << std::endl;
        }
        // 生产行为，将数据（信息）放入生产者的生产缓冲区中
        void push(const std::string &message)
        {
            // 停止，就不执行了
            if (!_run)
                return;
            if (_safe_type == AsyncLooperSafeType::SAFE_ASYNC_LOOPER)       // 安全模式下的处理
            {
                // 涉及_producer_buff缓冲区。在此处加锁比较好。
                // 防止在处理时，生产者缓冲区中有数据，而消费者执行交换缓冲区的操作
                std::unique_lock<std::mutex> ul(_mutex);
                // if(message.size() > _producer_buff.writeableSize())
                if (message.size() > _producer_buff.writeableSize())        // 写入的消息超过生产者缓冲区可写大小
                {
                    // std::cout << "AsyncLooper push, SAFE_ASYNC_LOOPER, 所输入的message超过_producer_buff.writeableSize(), _producer_cond尝试等待" << std::endl;

                    // return;

                    // std::cout << "_producer_buff.readableSize(): " << _producer_buff.readableSize()
                    // << " _producer_buff.writeableSize(): " << _producer_buff.writeableSize()
                    // << " _producer_buff._buffer.size():" << _producer_buff._buffer.size() << std::endl;

                    // std::cout << "_consumer_buff.readableSize(): " << _consumer_buff.readableSize()
                    // << " _consumer_buff.writeableSize(): " << _consumer_buff.writeableSize()
                    // << " _consumer_buff._buffer.size():" << _consumer_buff._buffer.size() << std::endl;

                    _consumer_cond.notify_all();            // 尝试操作，即唤醒消费者进行处理，再交换缓冲区
                    {
                        // std::unique_lock<std::mutex> ul(_mutex);
                        _producer_cond.wait(ul, [&]
                                            { return _producer_buff.writeableSize() >= message.size(); });
                    }

                    // 尝试失败了，那报错吧。但可能不会执行到这里，而是阻塞在上面的条件变量那
                    if (message.size() > _producer_buff.writeableSize())
                    {
                        // 等待失败再进行输出，美观。且当输出只有到标准输出时，尝试等待和等待成功的消息输出可能会影响观察输出结果
                        std::cout << "AsyncLooper push, SAFE_ASYNC_LOOPER, 所输入的message超过_producer_buff.writeableSize(), _producer_cond尝试等待失败,报错" << std::endl;
                        abort(); // 直接报错退出，如果只是return;，结果会丢失，该条消息不会push进_producer_buff
                    }
                    // else
                    // {
                    //     // std::cout << "AsyncLooper push, SAFE_ASYNC_LOOPER, 所输入的message超过_producer_buff.writeableSize(), _producer_cond尝试等待成功,继续运行" << std::endl;
                    // }
                }

                // 上方if结束，压入数据
                // _producer_cond.wait(ul, [&]{    return _producer_buff.writeableSize() >= message.size(); });
                _producer_buff.push(message.c_str(), message.size());
            }
            else            // 不安全的模式
            {
                // std::cout << "AsyncLooper push before, _producer_buff.readableSize: " <<  _producer_buff.readableSize() << std::endl;
                // 加锁，更安全些，防止在push数据时双缓冲区进行交换
                std::unique_lock<std::mutex> ul(_mutex);
                // 使用else，到此处_safe_type是UNSAFE_ASYNC_LOOPER，不加条件等待，因为安全情况（SAFE_ASYNC_LOOPER）上方已经处理了，
                // 此处直接push即可，_producer_buff不够大时会进行扩容，加了条件等待，反而会阻塞住
                // _producer_cond.wait(ul, [&]{    return _producer_buff.writeableSize() >= message.size(); });
                _producer_buff.push(message.c_str(), message.size());
            }
            // std::cout << "AsyncLooper push over, _producer_buff.readableSize: " <<  _producer_buff.readableSize() << std::endl;
            _consumer_cond.notify_all();        // 唤醒消费者进行处理
        }
        void stop()     // 不想继续执行了，停止
        {
            _run = false;
            // 生产者和消费者都唤醒，处理剩下的东西
            _producer_cond.notify_all();
            _consumer_cond.notify_all();
        }
        void modifyLooperSafeType(AsyncLooperSafeType safe_type)        // 支持修改安全模式
        {
            _safe_type = safe_type;
        }

    private:
        // 异步线程，即消费者的处理函数，此处我设置的消费者只有一个，因为我感觉够了。如果不够则可能需要搞线程池的方法，即修改代码了
        void threadEntrance()
        {
            // std::cout << "AsyncLooper threadEntrance" <<std::endl;
            // std::cout << "threadEntrance thread _run: " << _run <<std::endl;

            while (true)
            {
                // std::cout << "AsyncLooper threadEntrance while loop" <<std::endl;
                {           // 方便自动释放锁
                    // std::cout << "AsyncLooper threadEntrance unique_lock before" <<std::endl;
                    std::unique_lock<std::mutex> ul(_mutex);
                    // std::cout << "threadEntrance thread _run: " << _run <<std::endl;

                    // std::cout << "AsyncLooper threadEntrance unique_lock after" <<std::endl;
                    // 注意不能让此函数比构造函数中初始化_run还早执行，否则此处会直接退出函数，不会陷入死循环
                    if (!_run && _producer_buff.empty())        // 出口函数
                        return;
                    // std::cout << "AsyncLooper threadEntrance while loop, _consumer_cond.wait before" <<std::endl;
                    // 等待生产者的数据
                    _consumer_cond.wait(ul, [&]
                                        { return !_producer_buff.empty() || !_run; }); // 第二个参数需要使用函数
                    // std::cout << "AsyncLooper threadEntrance while loop, _consumer_cond.wait after" <<std::endl;
                    // 双缓冲区的交换
                    _producer_buff.swap(_consumer_buff);
                }
                _producer_cond.notify_all();    // 生产者的缓冲区空间够了，唤醒生产者工作
                _callback(_consumer_buff);      // 回调，进行消费
                _consumer_buff.reset();         // 恢复处理，方便下次操作
            }
        }

    private:
        std::mutex _mutex;                          // 互斥锁，防止多线程的情况下出错
        std::condition_variable _producer_cond;     // 生产者的条件变量
        std::condition_variable _consumer_cond;     // 消费者的条件变量
        std::atomic<bool> _run;                     // 判断是否正在执行。用atomic，方便修改
        Buffer _producer_buff;              // 双缓冲区中的生产者缓冲区
        Buffer _consumer_buff;              // 双缓冲区中的消费者缓冲区
        functor _callback;                  // 回调函数，消费者真正的消费行为
        AsyncLooperSafeType _safe_type;     // 安全类型
        // thread放在最后，否则当执行构造函数初始化_run时，_thread中的threadEntrance早就跑完返回了
        std::thread _thread;                // 执行回调函数的异步线程
    };
}

#endif